package com.oig.sys.base.service.impl;

import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysPageInfo;
import com.oig.common.module.SysRequest;
import com.oig.sys.base.dao.ITbPortDao;
import com.oig.sys.base.dto.PortDto;
import com.oig.sys.base.entity.TbPort;
import com.oig.sys.base.service.ITbPortService;
import com.oig.sys.base.vo.PortVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wuxl
 */
@Slf4j
@Service
public class TbPortServiceImpl implements ITbPortService {

	private final ITbPortDao tbPortDao;

	@Autowired
	public TbPortServiceImpl(ITbPortDao tbPortDao){
		 this.tbPortDao = tbPortDao;
	}

	@Transactional
	@Override
	public PortVo addPort(PortDto portDto){
		TbPort tbPort = this.tbPortDao.getByName(portDto.getName()) ;
		if (tbPort!=null){
			throw new BadRequestException("名称重复") ;
		}
		tbPort = new TbPort();
		BeanUtils.copyProperties(portDto, tbPort);
		tbPort.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		if (portDto.getSort()==null){
			tbPort.setSort(9999);
		}
		this.tbPortDao.save(tbPort);
		return this.buildVo(tbPort);
	}



	private PortVo buildVo(TbPort tbPort){
		PortVo portVo = new PortVo();
		BeanUtils.copyProperties(tbPort, portVo);
		return portVo;
	}

	@Transactional
	@Override
	public PortVo updatePort(PortDto portDto){
		TbPort port = this.tbPortDao.getByCode(portDto.getCode()) ;
		if (port==null){
			throw new BadRequestException("港口不存在");
		}
		if (!port.getName().equals(portDto.getName())){
			TbPort tbPort = this.tbPortDao.getByName(portDto.getName()) ;
			if (tbPort!=null){
				throw new BadRequestException("名称重复") ;
			}
		}
		BeanUtils.copyProperties(portDto, port);
		this.tbPortDao.save(port);
		return this.buildVo(port);
	}

	@Transactional
	@Override
	public PortVo cancelPort(String code){
		TbPort port = this.tbPortDao.getByCode(code) ;
		if (port==null){
			throw new BadRequestException("港口不存在");
		}
		if (CommonEnum.CommonStatusEnum.DEL.getCode().equals(port.getStatus())){
			throw new BadRequestException("港口已删除");
		}
		port.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
		this.tbPortDao.save(port) ;
		return this.buildVo(port) ;
	}

	@Override
	public List<PortVo> findPort(SysRequest<PortDto> portRequest) {
		if (portRequest.getParamDto()!=null){
			return this.queryPort(portRequest).getList();
		} else {
			Page<TbPort> page = this.tbPortDao.findAll(portRequest.pageable());
			List<PortVo> portVoList = new ArrayList<>();
			for (TbPort port : page.getContent()){
				portVoList.add(buildVo(port));
			}
			return portVoList;
		}
	}

	@Override
	public SysPageInfo<PortVo> queryPort(SysRequest<PortDto> portRequest) {
		Pageable pageable = portRequest.pageable();
		Page<TbPort> page ;
		Specification<TbPort> specification = new Specification<TbPort>() {
			@Override
			@Nullable
			public Predicate toPredicate(@NonNull Root<TbPort> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
				List<Predicate> predicateList = new ArrayList<>();

				if (StrUtil.isNotBlank(portRequest.getParamDto().getCityCode())) {
					Path<String> cityCodePath = root.get("cityCode");
					predicateList.add(criteriaBuilder.equal(cityCodePath, portRequest.getParamDto().getCityCode()));
				}
				if (StrUtil.isNotBlank(portRequest.getParamDto().getCountryCode())) {
					Path<String> countryCodePath = root.get("countryCode");
					predicateList.add(criteriaBuilder.equal(countryCodePath, portRequest.getParamDto().getCountryCode()));
				}
				if (StrUtil.isNotBlank(portRequest.getParamDto().getCode())) {
					Path<String> codePath = root.get("code");
					predicateList.add(criteriaBuilder.equal(codePath, portRequest.getParamDto().getCode()));
				}
				if (StrUtil.isNotBlank(portRequest.getParamDto().getName())) {
					log.debug("name={}",portRequest.getParamDto().getName());
					Path<String> namePath = root.get("name");
					predicateList.add(criteriaBuilder.like(namePath, "%" + portRequest.getParamDto().getName() + "%"));
				}
				if (StrUtil.isNotBlank(portRequest.getParamDto().getSearchWord())) {
					String searchWord = "%" + portRequest.getParamDto().getSearchWord() + "%";
					Path<String> customsCodePath = root.get("code");
					Path<String> customsNamePath = root.get("name");
					predicateList.add(criteriaBuilder.or(criteriaBuilder.like(customsCodePath, searchWord),
							criteriaBuilder.like(customsNamePath, searchWord)));
				}
				if (StrUtil.isNotBlank(portRequest.getParamDto().getEnName())) {
					log.debug("name={}",portRequest.getParamDto().getEnName());
					Path<String> namePath = root.get("enName");
					predicateList.add(criteriaBuilder.like(namePath, "%" + portRequest.getParamDto().getEnName() + "%"));
				}
				Path<String> statusPath = root.get("status");
				if (StrUtil.isNotBlank(portRequest.getParamDto().getStatus())) {
					log.debug("status={}",portRequest.getParamDto().getName());
					predicateList.add(criteriaBuilder.equal(statusPath, portRequest.getParamDto().getStatus()));
				} else {
					predicateList.add(criteriaBuilder.equal(statusPath, CommonEnum.CommonStatusEnum.OK.getCode()));
				}
				criteriaQuery.where(predicateList.toArray(new Predicate[0]));
				return criteriaQuery.getRestriction();
			}
		};
		page = this.tbPortDao.findAll(specification,pageable);
		List<PortVo> portVoList = new ArrayList<>();
		for (TbPort port : page.getContent()){
			portVoList.add(buildVo(port));
		}
		SysPageInfo<PortVo> sysPageInfo = new SysPageInfo<>(portRequest.getPageNo(), portRequest.getPageSize(), page.getTotalElements());
		sysPageInfo.setList(portVoList);
		return sysPageInfo;
	}

	@Override
	public PortVo getByName(String name) {
		TbPort port = this.tbPortDao.getByName(name);
		if (port!=null){
			return buildVo(port);
		}
		return null;
	}

	@Override
	public PortVo getByOldCode(String oldCode) {
		TbPort port = this.tbPortDao.getByOldCode(oldCode);
		if (port!=null){
			return buildVo(port);
		}
		return null;
	}

	@Override
	public PortVo getByCode(String code) {
		TbPort port = this.tbPortDao.getByCode(code);
		if (port!=null){
			return buildVo(port);
		}
		return null;
	}
}
